using System;
using System.Collections.Generic;
using System.Text;

using System.Net.Sockets;

using System.Threading;

using System.Xml;

namespace OWED.Network
{
    /// <summary>
    /// Takes a request and returns a reply
    /// All XmlDocuments as per the protocol
    /// </summary>
	/// <param name="connection">This object is persistent to each
	///							connection.  It is for external use.</param>
	///	<param name="number">The number of your message in line.</param>
    delegate XmlDocument ServerHandler (XmlDocument request, 
										ref object connection,
										int number);

    class ConnectionServer:IDisposable
    {
        //TODO:  Server class like Connection.cs
        TcpListener my_socket;
        ServerHandler my_handle;
        int my_port;

        Thread my_accepter;
        public ConnectionServer(int port, ServerHandler handle)
        {
            my_port = port;
            my_handle = handle;
            my_socket = new TcpListener(System.Net.IPAddress.Any, port);
            my_socket.Start();
            my_accepter = new Thread(Listen);
            my_accepter.IsBackground = true;
            my_accepter.Start(my_socket);
        }

        /// <summary>
        /// Launch new threads on new connections.
        /// </summary>
        /// <param name="ignore"></param>
        private void Listen(object listener) {
            try {
                while (true) {
                    Socket sock = (listener as TcpListener).AcceptSocket();
                    Thread t1 = new Thread(GetMessages);
                    t1.IsBackground = true;
                    t1.Start(sock);
                }
            } catch (SocketException) {
                Console.WriteLine("ConnectionServer no longer listening");
            }
        }

        private void GetMessages(object sock)
        {
            try {
                Socket socket = sock as Socket;
                Connection.Receive(socket, ProcessMessage, null);
            } catch (SocketException) {
                Console.WriteLine("Socket closed");
            }
        }

        /// <summary>
        /// Call the handler with the message in a new thread.
        /// Keeps it open for multiple messages.
        /// </summary>
        /// <param name="sock">the socket connected</param>
        private void ProcessMessage(XmlDocument request,
                                    Socket socket,
									ref object connection,
									int number)
        {
            /* Getting the message out of our wrapper */
            XmlDocument doc = new XmlDocument();
            XmlNode ticket = request.SelectSingleNode("Message/Ticket");
            XmlNode ourmsg = ticket.PreviousSibling;
            if (ourmsg == null)
                ourmsg = ticket.NextSibling;

            doc.AppendChild(doc.ImportNode(ourmsg, true));

            XmlDocument reply = my_handle(doc, ref connection, number);


            /*Wrapping the reply */
            XmlDocument wire_reply = new XmlDocument();
            wire_reply.AppendChild(wire_reply.CreateElement("Message"));
            wire_reply.FirstChild.AppendChild(
                wire_reply.ImportNode(reply.FirstChild, true));
            wire_reply.FirstChild.AppendChild(
                wire_reply.ImportNode(ticket, true));
            /* done wrapping */


            /* Sending reply: */
            StringBuilder datFactory = new StringBuilder();
            XmlWriter wr = XmlWriter.Create(datFactory);
            wire_reply.Save(wr);

            UTF8Encoding encode = new UTF8Encoding();
            byte[] wire_dat = encode.GetBytes(datFactory.ToString());

            lock (this)
                socket.Send(wire_dat);
            /* Reply sent */

        }

        #region IDisposable Members

        public void Dispose()
        {
            my_socket.Stop();
            my_socket.Server.Close();
        }

        #endregion
    }
}

